package cn.txun05.csmall.product.aop;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Slf4j
@Aspect
@Component
public class TimeAspect {
    //ProceedingJoinPoint连接点：程序执行过程中的某个节点，一般是某个方法的调用，也可以是抛出了某个异常
    //PointCut切入点：可以匹配一个或者多个连接点的表达式
    //execution配置的就是切入点表达式，用于匹配某些方法
    //在切入点表达式中，可以使用通配符
    //-- 1个星号，任意1次匹配
    //-- 连续两个小数点，任意n次匹配，只能用于包或者参数列表
    //                 返回值类型
    //                    ---------------------------包名
    //                                                  类名
    //                                                    方法名
    //                                                        参数列表
    //另外，在表达式中，在方法的返回值的左侧还可以指定修饰符，修饰符是可选的
    //通知（Advice）注解
    //@Around：环绕，包裹连接点，你的代码在连接点之前和之后执行
    //@Before:只能在连接点之前执行
    //@After:只能在连接点之后执行
    //@AfterReturning:只能在成功的执行了连接点之后执行
    //@AtferThrowing:只能在连接点方法抛出异常之后执行
    //以上各Advice执行的情况大致如下：
    /*
    @Around开始
    try{
    @Before
    执行连接点
    @AfterReturning
    }catch(ThrowAble e){
    @AtferThrowing
    }finally{
    @After
    }
    @Around结束
     */
    @Around("execution(* cn.txun05.csmall.product.service.*.*(..))")//切入点
    //                   cn.txun05.csmall.service
    //                   cn.txun05.csmall.product.service
    //                   cn.txun05.csmall.prod.uct.service
    //注解也是典型的修饰符之一
    //其实，Spring MVC的统一异常处理、安全检查就是通过AOP实现
    public Object xxx(ProceedingJoinPoint pjp) throws Throwable{
        long start = System.currentTimeMillis();
        String targetClassName = pjp.getTarget().getClass().getName();//获取匹配方法所在的类
        String signtureName = pjp.getSignature().getName();//获取匹配的方法名
        Object[] args = pjp.getArgs();//匹配方法的参数列表
        //pjp.proceed()执行以上表达式匹配的方法，在本练习中，指的是某个Service的某个方法
        //注意-1：必须获取调用proceed方法返回的结果，作为当前切面方法的返回值
        //--如果没有获取返回值，相当于执行了连接点，却没有获取返回值
        //注意-2:如果你的切入点表达式基于Service层的方法，
        // 调用proceed方法时异常必须抛出，否则，Controller将无法知道Service抛出的异常
        //就无法向客户端响应错误信息
        //其实，你可以使用try--catch，捕获调用proceed方法的异常，但必须在catch中抛出异常
        Object result = null;
        try {
             result = pjp.proceed();
        }catch (Exception e){
            e.printStackTrace();
            throw  e;
        }
        long end = System.currentTimeMillis();
        System.out.println(targetClassName+"类"
                +targetClassName+":"+Arrays.toString(args) +"方法执行耗时："
                +(end-start)+"毫秒");
        return  result;
    }
}
